#include "file_transfer_client.h"

// 可靠发送
int sendn(int sockfd, char *p, int len){
    int cursize = 0;
    while(cursize < len){
        ssize_t sret = send(sockfd, p + cursize, len - cursize, 0);
        cursize += sret;
    }
    return 0;
}

// 可靠接收
int recvn(int sockfd, char *p, int len){
    int cursize = 0;
    while(cursize < len){
        ssize_t sret = recv(sockfd, p + cursize, len - cursize, 0);
        cursize += sret;
    }
    return 0;
}

//获取已收文件大小
int isFileExist(char *filename) {
    DIR *dir = opendir(getcwd(NULL, 0)); // 打开当前目录
    struct dirent *entry;
    struct stat statbuf;
    while((entry = readdir(dir)) != NULL) {
        if(strcmp(entry->d_name, filename) == 0) { // 查找同名文件
            stat(entry->d_name, &statbuf);         // 获取文件状态
            closedir(dir);
            return statbuf.st_size; // 返回文件大小（存在）
        }
    }
    closedir(dir);
    return 0; // 文件不存在
}

int gets_client(int sockfd, char *filename){
    tlvPacket packet;
    // 发送文件名
    memset(&packet, 0, sizeof(packet));
    int exist_size = isFileExist(filename);
    packet.type = exist_size;
    packet.length = strlen(filename);
    memcpy(packet.value, filename, packet.length);
    sendn(sockfd, (char *)&packet, sizeof(packet.type) + sizeof(packet.length) + packet.length);

    //接收文件是否存在的错误信息
    recvn(sockfd, (char *)&packet.type, sizeof(packet.type));
    recvn(sockfd, (char *)&packet.length, sizeof(packet.length));
    recvn(sockfd, packet.value, packet.length);
    if(packet.type == -1){
        printf("%s\n",packet.value);
        return -1;
    }

    // 打开/创建文件
    umask(0);
    int fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0666);

    if(packet.type > 0){
        lseek(fd, exist_size, SEEK_SET);
    }

    // 接受文件大小
    off_t filesize;
    memset(&packet, 0, sizeof(packet));
    recvn(sockfd, (char *)&packet.type, sizeof(packet.type));
    recvn(sockfd, (char *)&packet.length, sizeof(packet.length));
    recvn(sockfd, packet.value, packet.length);
    filesize = *((int*)packet.value);

    ftruncate(fd,filesize);

    if(filesize > 104857600){
        size_t page_size = sysconf(_SC_PAGESIZE);
        off_t aligned_exist_size =(exist_size / page_size)*page_size;
        off_t offset_in_page = exist_size - aligned_exist_size;
        //实际映射长度
        size_t map_length = filesize - aligned_exist_size;

        char*map = (char*)mmap(NULL,map_length,PROT_WRITE,MAP_SHARED,fd,aligned_exist_size);
        ERROR_CHECK(map,MAP_FAILED,"mmap");
        recvn(sockfd,map+offset_in_page,filesize-exist_size);
        //recvn(sockfd,map,map_length);
        munmap(map,map_length);
    }else{
        // 接收文件内容
        while(1){
            memset(&packet, 0, sizeof(packet));
            recvn(sockfd, (char *)&packet.type, sizeof(packet.type));
            recvn(sockfd, (char *)&packet.length, sizeof(packet.length));
            if(packet.length > 0){
                recvn(sockfd, packet.value, packet.length);
                write(fd, packet.value, packet.length);
            }
            else{
                break;
            }

        }

    }
    close(fd);
    printf("recv complete\n");
    return 0;
}

int puts_client(int sockfd, char *filename){
    tlvPacket packet;
    //判断文件名错误的处理
    memset(&packet,0,sizeof(packet));
    packet.type = 1;
    if(access(filename,F_OK) != 0){
        printf("filename error, please input again!\n");
        packet.type = -1;
        const char error_message[1000] = "filename error, please input again!";
        memcpy(packet.value,error_message,sizeof(packet.value));
        packet.length = strlen(packet.value);
        sendn(sockfd, (char *)&packet, sizeof(packet.type)+sizeof(packet.length) + packet.length);
        return -1;
    }
    sendn(sockfd, (char *)&packet, sizeof(packet.type)+sizeof(packet.length) + packet.length);

    memset(&packet, 0, sizeof(packet));
    // 发送文件名
    packet.length = strlen(filename);
    memcpy(packet.value, filename, packet.length);
    sendn(sockfd, (char *)&packet, sizeof(packet.type) + sizeof(packet.length) + packet.length);

    //接收已传文件字节数
    recvn(sockfd,(char*)&packet.type,sizeof(packet.type));
    int exist_size = packet.type;
    recvn(sockfd, (char *)&packet.length, sizeof(packet.length));
    recvn(sockfd, packet.value, packet.length);

    // 可读打开上传文件
    int fd = open(filename,O_RDONLY);

    // 获取上传文件大小
    struct stat statbuf;
    fstat(fd, &statbuf);
    off_t filesize = statbuf.st_size;

    // 发送文件大小
    memset(&packet, 0, sizeof(packet));
    packet.length = sizeof(filesize);
    memcpy(packet.value, &filesize, packet.length);
    sendn(sockfd, (char *)&packet, sizeof(packet.type)+sizeof(packet.length) + packet.length);

    if(packet.type > 0){
        lseek(fd, exist_size, SEEK_SET);
    }

    if(filesize > 104857600){
        size_t page_size = sysconf(_SC_PAGESIZE);
        off_t aligned_exist_size =(exist_size / page_size)*page_size;
        off_t offset_in_page = exist_size - aligned_exist_size;
        //实际映射长度
        size_t map_length = filesize - aligned_exist_size;

        char*map = (char*)mmap(NULL,map_length,PROT_READ,MAP_PRIVATE,fd,aligned_exist_size);
        ERROR_CHECK(map,MAP_FAILED,"mmap");
        sendn(sockfd,map+offset_in_page,filesize-exist_size);
        //sendn(sockfd,map,map_length);
        munmap(map,map_length);

    }else{
        while(1){
            memset(&packet, 0, sizeof(packet));
            packet.length = read(fd, packet.value, sizeof(packet.value));
            if(packet.length > 0){
                sendn(sockfd, (char *)&packet, sizeof(packet.type)+sizeof(packet.length) + packet.length);
            }else{
                break;
            }
        }

        // 发送结束标志
        packet.length = 0;
        sendn(sockfd, (char *)&packet,sizeof(packet.type)+sizeof(packet.length) + packet.length);
    }
    close(fd);
    printf("send complete\n");
    return 0;
}


